#include <stdlib.h>
#include <stdio.h>
#include <limits.h>

#include "ptrreg.h"


typedef struct PointerEntry_tag {
	unsigned long key;
	void *value;
	struct PointerEntry_tag *prev;
	struct PointerEntry_tag *next;
} PointerEntry;


static PointerEntry* ptrreg_entry_new(unsigned long key, void *value);
static void ptrreg_initialize_entry(PointerEntry *entry);
static void ptrreg_entry_free(PointerEntry *entry);
static unsigned long ptrreg_next_key(void);
static unsigned long ptrreg_set_entry(unsigned long key, void *value);
static PointerEntry* ptrreg_get_entry(unsigned long key);

#define ptrreg_registry_entry_index_of(entry_key) (entry_key % PTRREG_REGISTRY_ENTRY_SIZE)

#define PTRREG_REGISTRY_ENTRY_SIZE 1000
static PointerEntry *registry[PTRREG_REGISTRY_ENTRY_SIZE];

unsigned long entry_key = 0;

unsigned long
ptrreg_set(void *value)
{
	unsigned long key = ptrreg_next_key();
	return ptrreg_set_entry(key, value);
}

void*
ptrreg_get(unsigned long key)
{
	PointerEntry *entry = ptrreg_get_entry(key);
	return (entry == NULL) ? NULL : entry->value;
}

void*
ptrreg_remove(unsigned long key) 
{
	void *value;
	int index = ptrreg_registry_entry_index_of(key);
	PointerEntry *entry = ptrreg_get_entry(key);

	if (entry == NULL) { return NULL; } 

	value = entry->value;
	if (entry->prev == NULL)
	{
		if (entry->next != NULL) { entry->next->prev = NULL; }
		registry[index] = entry->next;
	} else {
		if (entry->next == NULL)
		{
			entry->prev->next = NULL;
		} else {
			entry->prev->next = entry->next;
			entry->next->prev = entry->prev;
		}
	}
	ptrreg_entry_free(entry);
	return value;
}

unsigned long ptrreg_key_limit(void)
{
	return ULONG_MAX;
}

void ptrreg_reset_key(void)
{
	entry_key = 0;
}


static PointerEntry* 
ptrreg_entry_new(unsigned long key, void *value)
{
	PointerEntry *entry = malloc(sizeof(PointerEntry));
	if (entry == NULL) { return NULL; }
	
	ptrreg_initialize_entry(entry);
	entry->key = key;
	entry->value = value;
	return entry;
}

static void
ptrreg_initialize_entry(PointerEntry *entry)
{
	entry->key = 0;
	entry->value = NULL;
	entry->prev = NULL;
	entry->next = NULL;
}

static void
ptrreg_entry_free(PointerEntry *entry)
{
	ptrreg_initialize_entry(entry);
	free(entry);
}

static unsigned long
ptrreg_next_key(void)
{
	return ++entry_key;
}

static unsigned long
ptrreg_set_entry(unsigned long key, void *value)
{
	int index = ptrreg_registry_entry_index_of(key);
	PointerEntry *regEntry = registry[index];
	PointerEntry *newEntry = NULL;

	while ((regEntry != NULL) && (regEntry->next != NULL))
	{
		regEntry = regEntry->next;
	}

	newEntry = ptrreg_entry_new(key, value);
	if (newEntry == NULL) { return 0; }

	if (regEntry == NULL)
	{
		registry[index] = newEntry;
	} else {
		regEntry->next = newEntry;
		newEntry->prev = regEntry;
	}

	return key;
}

static PointerEntry* 
ptrreg_get_entry(unsigned long key)
{
	PointerEntry *regEntry = registry[ptrreg_registry_entry_index_of(key)];

	while ((regEntry != NULL) && (regEntry->key != key)) 
	{
		regEntry = regEntry->next; 
	}

	return regEntry;
}

